package net.sourceforge.pinyin4j;

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class PinyinHelper {

	/**
	 * A class encloses common constants
	 * 
	 * @author Li Min (xmlerlimin@gmail.com)
	 */
	private class Field {
		static final String LEFT_BRACKET = "(";

		static final String RIGHT_BRACKET = ")";

		static final String COMMA = ",";
	}

	/**
	 * A hash table contains <UnicodeValue, HanyuPinyinString> pairs
	 */
	static private Properties unicodeToHanyuPinyinTable = null;

	static {
		if (null == unicodeToHanyuPinyinTable) {
			initializeTable();
		}
	}

	/**
	 * Initialize the hash table contains <UnicodeValue, HanyuPinyinString>
	 * pairs
	 */
	static private void initializeTable() {
		try {
			Resource re=new ClassPathResource("unicode_to_hanyu_pinyin.txt");
			unicodeToHanyuPinyinTable = new Properties();
			unicodeToHanyuPinyinTable.load(new BufferedInputStream(re.getInputStream()));
		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	static public String[] toHanyuPinyinStringArray(char ch,
			HanyuPinyinOutputFormat outputFormat) {

		int codepointOfChar = String.valueOf(ch).codePointAt(0);

		return getHanyuPinyinStringArray(codepointOfChar, outputFormat);
	}

	/**
	 * Get the formatted Hanyu Pinyin representations of the given codepoint in
	 * array format.
	 * 
	 * @param codepoint
	 *            The codepoint value of an Unicode character
	 * @param outputFormat
	 *            Describes the desired format of returned Hanyu Pinyin string
	 * @return The formatted Hanyu Pinyin representations of the given codepoint
	 *         in array format; return null if no record is found in the
	 *         hashtable.
	 */
	static private String[] getHanyuPinyinStringArray(int codepoint,
			HanyuPinyinOutputFormat outputFormat) {

		String pinyinRecord = getHanyuPinyinRecord(codepoint);

		if (null != pinyinRecord) {
			int indexOfLeftBracket = pinyinRecord.indexOf(Field.LEFT_BRACKET);
			int indexOfRightBracket = pinyinRecord
					.lastIndexOf(Field.RIGHT_BRACKET);

			String stripedString = pinyinRecord.substring(
					indexOfLeftBracket + 1, indexOfRightBracket);

			if (HanyuPinyinVCharType.WITH_V == outputFormat.getVCharType()) {
				stripedString = stripedString.replaceAll("u:", "v");
			}

			if (HanyuPinyinToneType.WITHOUT_TONE == outputFormat.getToneType()) {
				stripedString = stripedString.replaceAll("[c]", "");
			}

			if (HanyuPinyinCaseType.UPPERCASE == outputFormat.getCaseType()) {
				stripedString = stripedString.toUpperCase();
			}

			return stripedString.split(Field.COMMA);
		} else {
			// no record found
			return null;
		}
	}

	/**
	 * Fetch Hanyu Pinyin record from <UnicodeValue, HanyuPinyinString> table by
	 * given codepoint
	 * 
	 * @param codepoint
	 *            The codepoint value of an Unicode character
	 * @return return the raw Hanyu Pinyin record of the given codepoint; return
	 *         null if no record is found in the hashtable.
	 */
	private static String getHanyuPinyinRecord(int codepoint) {

		String codepointHexStr = Integer.toHexString(codepoint).toUpperCase();

		// fetch from hashtable
		String foundRecord = unicodeToHanyuPinyinTable
				.getProperty(codepointHexStr);

		if ((null != foundRecord) && foundRecord.startsWith(Field.LEFT_BRACKET)
				&& foundRecord.endsWith(Field.RIGHT_BRACKET)) {

			return foundRecord;
		} else {
			// no record is found or bad formatted record only
			return null;
		}
	}

	/**
	 * Get a string which all Chinese characters are replaced by corresponding
	 * main (first) Hanyu Pinyin representation.
	 * 
	 * <p>
	 * <b>Special Note</b>: If the return contains "none0", that means that
	 * Chinese character is in Unicode CJK talbe, however, it has not
	 * pronounciation in Chinese
	 * 
	 * @param str
	 *            A given string contains Chinese characters
	 * @param outputFormat
	 *            Describes the desired format of returned Hanyu Pinyin string
	 * @param seperater
	 *            The string is appended after a Chinese character (excluding
	 *            the last Chinese character at the end of sentence). <b>Note!
	 *            Seperater will not appear after a non-Chinese character</b>
	 * @return a String identical to the original one but all recognizable
	 *         Chinese characters are converted into main (first) Hanyu Pinyin
	 *         representation
	 */
	static public String toHanyuPinyinString(String str,
			HanyuPinyinOutputFormat outputFormat, String seperater) {

		StringBuffer resultPinyinStrBuf = new StringBuffer();

		for (int i = 0; i < str.length(); i++) {
			int codepointOfChar = str.codePointAt(i);
			String mainPinyinStrOfChar = getFirstHanyuPinyinString(
					codepointOfChar, outputFormat);
            
			if (null != mainPinyinStrOfChar) {
				//��һ�� ��Ȼ���ܻ��һ������ ����������ص���
				resultPinyinStrBuf.append(mainPinyinStrOfChar.substring(0, mainPinyinStrOfChar.length()-1));
				if (i != str.length() - 1) { // avoid appending at the end
					//resultPinyinStrBuf.append(seperater);
				}
			} else {
				
				resultPinyinStrBuf.append(str.charAt(i));
			}
		}

		return resultPinyinStrBuf.toString();
	}

	static private String getFirstHanyuPinyinString(int codepoint,
			HanyuPinyinOutputFormat outputFormat) {

		String[] pinyinStrArray = getHanyuPinyinStringArray(codepoint,
				outputFormat);

		if ((null != pinyinStrArray) && (pinyinStrArray.length > 0)) {
			return pinyinStrArray[0];
		} else {
			return null;
		}
	}

	// ! Hidden constructor
	private PinyinHelper() {
	}
}
